package ia.robot.app;

import java.io.Serializable;

/**
 * Clase que representa un punto de coordenadas cartesianas.
 * 
 * @author Jaime A. Corrales Gonzalez
 * @author Alejandro Tejera Perez
 */
public class Point implements Cloneable, Serializable {

	private static final long serialVersionUID = 1L;

	public static final int NORTH = 0;
	public static final int EAST = 1;
	public static final int SOUTH = 2;
	public static final int WEST = 3;

	private int x;
	private int y;

	// Constructores.

	/**
	 * Constructor generico.
	 */
	public Point() {
		this.x = 0;
		this.y = 0;
	}

	/**
	 * Constructor dadas las coordenadas.
	 * 
	 * @param x
	 *            - Coordenada x.
	 * @param y
	 *            - Coordenada y.
	 */
	public Point(int x, int y) {
		this.x = x;
		this.y = y;
	}

	// Getters.

	/**
	 * Retorna la coordenada x del punto.
	 * 
	 * @return Coordenada x.
	 */
	public int getX() {
		return this.x;
	}

	/**
	 * Retorna la coordenada y del punto.
	 * 
	 * @return Coordenada y.
	 */
	public int getY() {
		return this.y;
	}

	// Funciones de distancias.

	/**
	 * Funcion que calcula la distancia euclidea entre dos puntos.
	 * 
	 * @param p
	 *            - Punto a comparar.
	 * @return Distancia euclidea.
	 */
	public double euclideanDistance(Point p) {
		int xDiff = Math.abs(this.x - p.x);
		int yDiff = Math.abs(this.y - p.y);

		return Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2));
	}
	
	/**
	 * Funcion que calcula la distancia Mahalanobis entre dos puntos.
	 * 
	 * @param p
	 *            - Punto a comparar.
	 * @return Distancia Mahalanobis.
	 */
	public double mahalanobisDistance(Point p) {
		return Math.max(Math.abs(this.x - p.x), Math.abs(this.y - p.y));
	}

	/**
	 * Funcion que calcula la distancia Manhattan entre dos puntos.
	 * 
	 * @param p
	 *            - Punto a comparar.
	 * @return Distancia Manhattan.
	 */
	public double manhattanDistance(Point p) {
		return Math.abs(this.x - p.x) + Math.abs(this.y - p.y);
	}

	// Funciones de direccion.

	/**
	 * Funcion para aplicar una direccion de movimiento
	 * 
	 * @param dir
	 *            - Direccion del movimiento.
	 * 
	 * @return Punto cuyas coordenadas son el resultado de aplicar la direccion
	 *         a las coordenadas de este punto.
	 */
	public Point displace(int dir) {
		switch (dir) {
		case NORTH:
			return new Point(this.x, this.y - 1);
		case SOUTH:
			return new Point(this.x, this.y + 1);
		case EAST:
			return new Point(this.x + 1, this.y);
		case WEST:
			return new Point(this.x - 1, this.y);
		default:
			return null;
		}
	}

	/**
	 * Funcion para obtener una direccion de movimiento.
	 * 
	 * @param p
	 *            - Punto destino.
	 * @return Direccion de desplazamiento (NORTH, EAST, SOUTH o WEST).
	 */
	public int displace(Point p) {
		int xdiff = x - p.x;
		int ydiff = y - p.y;
		if (xdiff == -1)
			return EAST;
		if (xdiff == 1)
			return WEST;
		if (ydiff == -1)
			return SOUTH;
		if (ydiff == 1)
			return NORTH;
		return 0;
	}

	// Funciones utiles.

	public Point clone() {
		return new Point(this.x, this.y);
	}

	/**
	 * Funcion que compara dos puntos.
	 * 
	 * @param p
	 *            - Punto a comparar.
	 * @return True si ambos puntos son iguales,
	 */
	public boolean equals(Object p) {
		if (p == null)
			return false;

		if ((this.x != ((Point) p).x) || (this.y != ((Point) p).y))
			return false;

		return true;
	}

	public String toString() {
		return new String("[x = " + this.x + ", y = " + this.y + "]");
	}
}
